﻿
using LitJson;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
#if UNITY_EDITOR
using UnityEditor;
#endif

namespace GameBoxTools
{
    /// <summary>
    /// 批量统计
    /// </summary>
    public class BaseBatchStatistic : MonoBehaviour
    {
        internal struct ResultData
        {
            public float time;
            public Vector3 angle;
            public List<float> result;
            public float average;
            public int frameCount;
			public float max_frame_overdraw;//overdraw单帧峰值

            public ResultData(float _t, Vector3 _a, List<float> _r, float _ave, int _fc, float _mfo)
            {
                this.time = _t;
                this.angle = _a;
                this.result = _r;
                this.average = _ave;
                this.frameCount = _fc;
                this.max_frame_overdraw = _mfo;
            }
        }

        internal struct AnalyzeDatas
        {
            public string prefab;
            public float max_average;//对应ma_angle整体平均最大值
            public Vector3 ma_angle;
            public float max_frame;//对应mo_angle下时最大峰值
            public Vector3 mo_angle;

            public AnalyzeDatas(string _p, float _ma, Vector3 _maa, float _mo, Vector3 _moa)
            {
                this.prefab = _p;
                this.max_average = _ma;
                this.ma_angle = _maa;
                this.max_frame = _mo;
                this.mo_angle = _moa;
            }
        }

        private JsonData data;
        private GameObject newGo = null;
        private Transform PSRoot;
        private Queue<string> psQueue = new Queue<string>();
        private int currentShowRendersKey = -1;//当前总体性能
        private List<int> currentShowRenders = new List<int>();//当前总体性能所包含Render
        private int rotateAngle = 90;//每次采样粒子旋转角度
        private int currentPSAngleY = -90;//当前粒子y角度
        private int rtCount = 0;//纹理数量

        private Dictionary<string, ResultData[]> dic = new Dictionary<string, ResultData[]>();
        private List<AnalyzeDatas> ads_list = new List<AnalyzeDatas>();

        //UI
        private Toggle collectToggle;
        private Text togTips;
        private Text result;
        private Button StartBtn;
        private Text ToolTips;
        private Text Process;
        private bool isStart = false;
        private bool isAllAngle = false;//采集四个角度（0, 90, 180, 270）
        private int currentIndex = 0;//当前在第几个角度
        private int angleCount = 4;
        private int listCount = 0;

        private BaseStatistic bs;
		
		#region 代码常量
        [Header("粒子信息导出json路径")]
        public string sourecjsonpath = "D:/2017/Tools/files/allExportAllParticleSystemPrefabFiles.json";
        [Header("1个角度粒子分析性能数据保存json路径")]
        public string save1Path = "D:/2017/Tools/files/overdraw.json";
        [Header("4个角度粒子分析性能数据保存json路径")]
        public string save4Path = "D:/2017/Tools/files/overdraw_4.json";
        [Header("1个角度粒子分析结果保存json路径")]
        public string analyze1Path = "D:/2017/Tools/files/overdraw_analyze.json";
        [Header("4个角度粒子分析结果保存json路径")]
        public string analyze4Path = "D:/2017/Tools/files/overdraw_analyze_4.json";

        string savePath;
        string analyzePath;
        #endregion

        [Header("分析选项(true:Overdraw, false:透明占比)")]
        public bool isOverdraw = true;//true：统计overdraw，false：统计透明像素占比
        [Header("求均值时去除头尾最高最低值百分比")]
        public int ignoreRate = 3;

        private void Start()
        {
            //格式化路径
            this.sourecjsonpath = this.sourecjsonpath.Replace('\\', '/');
            this.save1Path = this.save1Path.Replace('\\', '/');
            this.save4Path = this.save4Path.Replace('\\', '/');
            this.analyze1Path = this.analyze1Path.Replace('\\', '/');
            this.analyze4Path = this.analyze4Path.Replace('\\', '/');

            //UI
            var AllPsProcess = GameObject.Find("AllPsProcess");
            this.collectToggle = AllPsProcess.transform.Find("CollectToggle").GetComponent<Toggle>();
            this.togTips = AllPsProcess.transform.Find("CollectToggle").Find("tips").GetComponent<Text>();
            this.result = AllPsProcess.transform.Find("CollectToggle").Find("result").GetComponent<Text>();
            this.StartBtn = AllPsProcess.transform.Find("Start").GetComponent<Button>();
            this.ToolTips = AllPsProcess.transform.Find("ToolTips").GetComponent<Text>();
            this.Process = AllPsProcess.transform.Find("ToolTips").Find("Process").GetComponent<Text>();

            this.bs = GameObject.Find("PSRoot").GetComponent<BaseStatistic>();
            this.PSRoot = GameObject.Find("PSRoot").transform;
            this.PSRoot.localEulerAngles = Vector3.zero;
            this.data = JsonMapper.ToObject(File.ReadAllText(sourecjsonpath, Encoding.Default));

            Text title = AllPsProcess.transform.Find("Title").GetComponent<Text>();
            if (this.isOverdraw)
            {
                title.text = "批量分析粒子性能";
            }
            else
            {
                title.text = "批量分析粒子透明像素占比";
            }

            var count = data["prefabs"].Count;
            for(int i=0; i < count; ++i)
            {
                if (1 == (byte)data["prefabs"][i]["Ignore"])
                    continue;

                string prefabName = data["prefabs"][i]["prefab"].ToString();
                this.psQueue.Enqueue(prefabName);
            }
            this.listCount = this.psQueue.Count;

            this.Process.text = "进度: " + "0/" + this.listCount;

            this.result.text = "性能结果: " + save4Path + "\n分析结果: " + analyze4Path;
            this.collectToggle.onValueChanged.AddListener((ison) =>
            {
                if(ison)
                {
                    this.togTips.text = "将粒子分别使用0，90，180，270四个y轴旋转角度各自进行分析";
                    this.result.text = "性能结果: " + save4Path + "\n分析结果: " + analyze4Path;
                }
                else
                {
                    this.togTips.text = "粒子分析(y轴旋转为0)";
                    this.result.text = "性能结果: " + save1Path + "\n分析结果: " + analyze1Path;
                }
            });

            this.StartBtn.onClick.AddListener(() => 
            {
                AllPsProcess.transform.Find("ToolTips").gameObject.SetActive(true);
                this.isAllAngle = this.collectToggle.isOn;
                this.isStart = true;
                this.ProcessPs();
            });
        }

        //进度信息
        string getmsg()
        {
            string msg;
            int c = this.listCount - this.psQueue.Count;
            msg = "进度: " + c + "/" + this.listCount + "\n";
            msg += "粒子: " + newGo.name + "\n";
            msg += "角度: " + this.newGo.transform.localEulerAngles + "\n";
            msg += "时长: " + this.bs.GetCollectTime() + "s\n";
            return msg;
        }

        string[] dot = new string[] { "", ".", "..", "..." };
        int dotindex = 0;
        private void Update()
        {
            if (this.isStart)
            {
                ++dotindex;
                this.ToolTips.GetComponent<Text>().text = "数据分析中" + dot[dotindex % 4];
            }
        }

        //打印结果
        void printResult()
        {
            foreach(var item in this.dic)
            {
                foreach(var f in item.Value)
                {
                    string msg = item.Key;
                    msg += " angle: " + f.angle;
                    msg += " frame: " + f.frameCount;
                    msg += " average: " + f.average;
                    msg += " (" + f.result + ")";

                    Debug.Log(msg);
                }
            }
        }

        StringBuilder fdata = new StringBuilder();
        //保存性能分析结果到json
        void Save()
        {
            JsonData info = new JsonData();
            info["prefabs"] = new JsonData();

            foreach (var item in this.dic)
            {
                JsonData data = new JsonData();
                data["prefab"] = item.Key;
                data["data"] = new JsonData();
                foreach (var f in item.Value)
                {
                    JsonData result = new JsonData();
                    result["time"] = string.Format("{0}s", f.time);
                    result["angle"] = string.Format("({0}, {1}, {2})", f.angle.x, f.angle.y, f.angle.z);
                    result["frame"] = f.frameCount;
                    result["average"] = f.average;
                    fdata.Remove(0, fdata.Length);//clear
                    for(int frame = 0; frame < f.result.Count; ++frame)
                    {
                        fdata.Append(frame + 1);
                        fdata.Append("-");
                        fdata.Append(f.result[frame]);
                        fdata.Append(", ");
                    }
                    if (f.result.Count > 0)
                        fdata = fdata.Remove(fdata.Length - 2, 2);//删除最后的', '

                    result["result"] = fdata.ToString();

                    data["data"].Add(result);
                }

                info["prefabs"].Add(data);
            }

            savePath = save1Path;
            if (this.isAllAngle) savePath = save4Path;
            FileStream fs = new FileStream(savePath, FileMode.Create, FileAccess.ReadWrite);
            StreamWriter sw = new StreamWriter(fs); // 创建写入流

            JsonWriter w = new JsonWriter();
            w.PrettyPrint = true;
            w.IndentValue = 4;
            JsonMapper.ToJson(info, w);

            sw.Write(w.ToString());
            sw.Close(); //关闭文件
        }

        //分析结果集，得出性能ps列表
        void Analyze()
        {
            JsonData info = new JsonData();
            info["prefabs"] = new JsonData();

            foreach (var item in this.dic)
            {
                //保存最高平均值与最高峰值
                float _ma = 0.0f;
                Vector3 _maa = Vector3.zero;
                float _mo = 0.0f;
                Vector3 _moa = Vector3.zero;
                foreach (var f in item.Value)
                {
                    if(f.result.Count > 0)
                    {
                        if (_mo < f.max_frame_overdraw)
                        {
                            _mo = f.max_frame_overdraw;
                            _moa = f.angle;
                        }
                    }

                    if (_ma < f.average)
                    {
                        _ma = f.average;
                        _maa = f.angle;
                    }
                }

                AnalyzeDatas ad = new AnalyzeDatas(item.Key, _ma, _maa, _mo, _moa);
                this.ads_list.Add(ad);
            }

            this.ads_list.Sort((x, y) => { return y.max_frame.CompareTo(x.max_frame); });//按照最高峰值降序排列

            foreach (var item in this.ads_list)
            {
                JsonData data = new JsonData();
                fdata.Remove(0, fdata.Length);//clear
                fdata.Append("frame_max: ");
                fdata.Append(item.max_frame);
                fdata.Append(", ");
                fdata.Append("angle: ");
                fdata.Append(item.mo_angle.ToString());

                fdata.Append("   ");
                fdata.Append("average_max: ");
                fdata.Append(item.max_average);
                fdata.Append(", ");
                fdata.Append("angle: ");
                fdata.Append(item.ma_angle.ToString());

                data[item.prefab] = fdata.ToString();
                

                info["prefabs"].Add(data);
            }

            analyzePath = analyze1Path;
            if (this.isAllAngle) analyzePath = analyze4Path;
            FileStream fs = new FileStream(analyzePath, FileMode.Create, FileAccess.ReadWrite);
            StreamWriter sw = new StreamWriter(fs); // 创建写入流

            JsonWriter w = new JsonWriter();
            w.PrettyPrint = true;
            w.IndentValue = 4;
            JsonMapper.ToJson(info, w);

            sw.Write(w.ToString());
            sw.Close(); //关闭文件
        }

        public void ProcessPs()
        {
            System.GC.Collect();

            if (0 == this.psQueue.Count)//数据处理完成
            {
                if(isAllAngle)
                {
                    if(angleCount == currentIndex)
                    {
                        this.isStart = false;
                        this.Clear();
                        this.bs.Clear();
                        //printResult();
                        Save();
                        Analyze();
#if UNITY_EDITOR
                        System.GC.Collect();
                        Debug.Log("[<color=#00ff00>处理完成</color>]:\n性能结果: " + savePath + "\n分析结果: " + analyzePath);
                        EditorApplication.isPlaying = false;
#endif
                        return;
                    }
                }
                else
                {
                    this.isStart = false;
                    this.Clear();
                    this.bs.Clear();
                    //printResult();
                    Save();
                    Analyze();
#if UNITY_EDITOR
                    System.GC.Collect();
                    Debug.Log("[<color=#00ff00>处理完成</color>]:\n性能结果: " + savePath + "\n分析结果: " + analyzePath);
                    EditorApplication.isPlaying = false;
#endif
                    return;
                }
            }

            if(isAllAngle)
            {
                currentIndex %= angleCount;

                if(0 == currentIndex)//单粒子4个角度都处理完成
                {
                    this.Refresh();
                }

                this.RotatePs();
                ++currentIndex;
            }
            else
            {
                this.Refresh();
            }

            this.Process.text = this.getmsg();
            this.Clear();
            this.bs.ReCollect();
        }

        void Refresh()
        {
#if UNITY_EDITOR
            DestroyImmediate(newGo);
            newGo = null;

            var prefabName = this.psQueue.Dequeue();
            GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabName);
            if (null != prefab)
            {
                newGo = PrefabUtility.InstantiatePrefab(prefab) as GameObject;
                newGo.name = prefab.name;
                if (this.PSRoot) newGo.transform.SetParent(this.PSRoot, false);
            }

            this.bs.Init();
#endif
        }

        List<List<Area>> framedata = new List<List<Area>>();
        public void AddOriginData(List<Area> data)
        {
            framedata.Add(data);
        }

        void Clear()
        {
            foreach (var item in this.framedata)
            {
                item.Clear();
            }
            this.framedata.Clear();
        }

        public void Init(int rc = 0, int cc = 0)
        {
            Util.ClearConsole();
            this.currentShowRenders.Clear();
            this.currentShowRendersKey = -1;
            for (int i = 0; i < rc; ++i)
            {
                this.currentShowRenders.Add(i);
                this.currentShowRendersKey += (1 << i);
            }
            this.rtCount = cc;

            this.AnalyzeData(this.currentShowRenders.ToArray());

            this.ProcessPs();
        }
        
        void AnalyzeData(int[] renders)
        {
            ResultData[] data;
            if(!dic.ContainsKey(newGo.name))
            {
                if(isAllAngle)
                {
                    data = new ResultData[this.angleCount];
                }
                else
                {
                    data = new ResultData[1];
                }

                dic.Add(newGo.name, data);
            }
            data = this.dic[newGo.name];

            int frame = 0;
            List<float> result = new List<float>();
            if (0 != renders.Length)
            {
                foreach (var list in this.framedata)
                {
                    ++frame;
                    float frame_result = 0f;
                    if(this.isOverdraw)
                        frame_result = Util.calcOverdraw(list, renders, this.rtCount);
                    else
                        frame_result = Util.calcTransparent(list, renders, this.rtCount);
                    result.Add(frame_result);
                }
            }

            //去极值
            List<float> result_clone = new List<float>();
            result.ForEach(i => result_clone.Add(i));
            result_clone.Sort((x, y) => { return y.CompareTo(x); });//降序排列，list[0]为最大值
            float allrate = 0.0f;
            var temp_index = this.ignoreRate * 0.01f * result_clone.Count;//最高值与最低值各去除x%
            int start_index = (int)temp_index;
            int end_index = result_clone.Count - (int)temp_index;
            for(int i = start_index; i < end_index; ++i)
            {
                allrate += result_clone[i];
            }
            var average = allrate / (result_clone.Count - 2 * temp_index);

            var index = this.isAllAngle ? (currentIndex - 1) : currentIndex;
            data[index] = new ResultData(this.bs.GetCollectTime(), this.newGo.transform.localEulerAngles, result, average, frame, result_clone[0]);
        }

        //旋转粒子
        void RotatePs()
        {
            this.currentPSAngleY += this.rotateAngle;
            this.currentPSAngleY %= 360;
            this.newGo.transform.localEulerAngles = new Vector3(-30, this.currentPSAngleY, 0);
        }
    }
}
